package com.ikingtech.platform.business.workbench.controller;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ikingtech.framework.sdk.base.model.PageResult;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.enums.common.ElementTypeEnum;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.framework.sdk.workbench.api.ScheduleApi;
import com.ikingtech.framework.sdk.workbench.api.ScheduleUserApi;
import com.ikingtech.framework.sdk.workbench.model.ScheduleDTO;
import com.ikingtech.framework.sdk.workbench.model.ScheduleExecutorDTO;
import com.ikingtech.framework.sdk.workbench.model.ScheduleQueryParamDTO;
import com.ikingtech.framework.sdk.workbench.model.ScheduleUser;
import com.ikingtech.platform.business.workbench.entity.ScheduleDO;
import com.ikingtech.platform.business.workbench.entity.ScheduleExecutorDO;
import com.ikingtech.platform.business.workbench.service.ScheduleRepository;
import com.ikingtech.platform.business.workbench.service.ScheduleExecutorRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @author tie yan
 */
@RequiredArgsConstructor
@ApiController(value = "/schedule", name = "工作台-日程管理", description = "工作台-日程管理")
public class ScheduleController implements ScheduleApi {

    private final ScheduleRepository repo;

    private final ScheduleExecutorRepository scheduleExecutorRepo;

    private final ScheduleUserApi scheduleUserApi;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<String> add(ScheduleDTO schedule) {
        ScheduleDO entity = Tools.Bean.copy(schedule, ScheduleDO.class);
        entity.setId(Tools.Id.uuid());
        entity.setTenantCode(Me.tenantCode());
        this.repo.save(entity);
        ScheduleExecutorDO scheduleUserEntity = new ScheduleExecutorDO();
        scheduleUserEntity.setId(Tools.Id.uuid());
        scheduleUserEntity.setTenantCode(Me.tenantCode());
        scheduleUserEntity.setScheduleId(entity.getId());
        scheduleUserEntity.setExecutorId(Me.id());
        scheduleUserEntity.setExecutorType(ElementTypeEnum.USER.name());
        this.scheduleExecutorRepo.save(scheduleUserEntity);
        return R.ok(entity.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        this.repo.removeById(id);
        return R.ok();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> update(ScheduleDTO schedule) {
        if (!this.repo.exists(Wrappers.<ScheduleDO>lambdaQuery().eq(ScheduleDO::getId, schedule.getId()))) {
            throw new FrameworkException("对象不存在");
        }
        ScheduleDO entity = Tools.Bean.copy(schedule, ScheduleDO.class);
        this.repo.updateById(entity);
        return R.ok();
    }

    @Override
    public R<ScheduleDTO> detail(String id) {
        ScheduleDO entity = this.repo.getById(id);
        if (null == entity) {
            throw new FrameworkException("对象不存在");
        }
        ScheduleDTO schedule = Tools.Bean.copy(entity, ScheduleDTO.class);
        return R.ok(schedule);
    }

    @Override
    public R<List<ScheduleDTO>> page(ScheduleQueryParamDTO queryParam) {
        List<String> scheduleIds = !Boolean.FALSE.equals(queryParam.getLoginUser()) ? this.filterScheduleIdByLoginUser() : new ArrayList<>();
        if (!Boolean.FALSE.equals(queryParam.getLoginUser()) && Tools.Coll.isBlank(scheduleIds)) {
            return R.ok(Collections.emptyList());
        }
        queryParam.setIds(scheduleIds);
        return R.ok(PageResult.build(this.repo.page(new Page<>(queryParam.getPage(), queryParam.getRows()), this.repo.createWrapper(queryParam))).convertBatch(this::modelConvert));
    }

    private List<String> filterScheduleIdByLoginUser() {
        ScheduleUser scheduleUser = this.scheduleUserApi.loadById(Me.id());
        List<String> executorIds = new ArrayList<>();
        executorIds.addAll(scheduleUser.getUserDeptIds());
        executorIds.addAll(scheduleUser.getUserPostIds());
        executorIds.addAll(scheduleUser.getUserRoleIds());
        executorIds.add(Me.id());
        if (Tools.Coll.isBlank(executorIds)) {
            return Collections.emptyList();
        }
        return this.scheduleExecutorRepo.listObjs(Wrappers.<ScheduleExecutorDO>lambdaQuery()
                .select(ScheduleExecutorDO::getScheduleId)
                .in(ScheduleExecutorDO::getExecutorId, executorIds));
    }

    @Override
    public R<List<ScheduleDTO>> all() {
        return R.ok(this.modelConvert(this.repo.list()));
    }

    @Override
    public R<Map<String, Integer>> countByDate(ScheduleQueryParamDTO queryParam) {
        List<String> scheduleIds = !Boolean.FALSE.equals(queryParam.getLoginUser()) ? this.filterScheduleIdByLoginUser() : new ArrayList<>();
        if (!Boolean.FALSE.equals(queryParam.getLoginUser()) && Tools.Coll.isBlank(scheduleIds)) {
            return R.ok(Collections.emptyMap());
        }
        queryParam.setIds(scheduleIds);
        List<ScheduleDO> entities = this.repo.list(ScheduleRepository.createWrapper(queryParam));
        Map<String, Integer> result = new HashMap<>();
        entities.forEach(entity -> {
            String dateStr = Tools.DateTime.Formatter.simpleDate(entity.getEstimateStartTime().toLocalDate());
            Integer count = result.computeIfAbsent(dateStr, k -> 0);
            result.put(dateStr, count + 1);
        });
        return R.ok(result);
    }

    private List<ScheduleDTO> modelConvert(List<ScheduleDO> entities) {
        if (Tools.Coll.isBlank(entities)) {
            return Collections.emptyList();
        }
        List<ScheduleExecutorDO> executorEntities = this.scheduleExecutorRepo.list(Wrappers.<ScheduleExecutorDO>lambdaQuery().in(ScheduleExecutorDO::getScheduleId, Tools.Coll.convertList(entities, ScheduleDO::getId)));
        Map<String, List<ScheduleExecutorDO>> executorMap = Tools.Coll.convertGroup(executorEntities, ScheduleExecutorDO::getScheduleId);
        return Tools.Coll.convertList(entities, entity -> this.modelConvert(entity, executorMap.get(entity.getId())));
    }

    private ScheduleDTO modelConvert(ScheduleDO entity, List<ScheduleExecutorDO> executorEntities) {
        ScheduleDTO schedule = Tools.Bean.copy(entity, ScheduleDTO.class);
        if (null != schedule.getType()) {
            schedule.setTypeName(schedule.getType().description);
        }
        if (null != schedule.getStatus()) {
            schedule.setStatusName(schedule.getStatus().description);
        }
        if (Tools.Coll.isNotBlank(executorEntities)) {
            schedule.setExecutors(Tools.Coll.convertList(executorEntities, executorEntity -> {
                ScheduleExecutorDTO executor = Tools.Bean.copy(executorEntity, ScheduleExecutorDTO.class);
                if (null != executor.getExecutorType()) {
                    executor.setExecutorTypeName(executor.getExecutorType().description);
                }
                return executor;
            }));
        }
        return schedule;
    }
}